﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.IO;
using System.Drawing;

namespace demo_cs
{
    public partial class FormDemo : Form
    {
        private enum ControlType
        {
            Camra,
            Model,
            //Light,
        }

        private enum SceneControl
        {
            MoveForward,
            MoveBackward,
            MoveLeft,
            MoveRight,
            MoveUp,
            MoveDown,            
            RotateLeft,
            RotateRight,
            RotateUp,
            RotateDown,
            RollLeft,
            RollRight,
            ScaleInX,
            ScaleOutX,
            ScaleInY,
            ScaleOutY,
            ScaleInZ,
            ScaleOutZ,
            ZoomIn,
            ZoomOut,
            Focus,
        }

        private DemoContext _demo;

        private Cvars _cvars;
    
        private ControlType _currentControl;

        private List<string> _loadedCameraNames = new List<string>();

        private List<CameraType> _loadedCameraTypes = new List<CameraType>();

        private List<string> _loadedModelFiles = new List<string>();

        private bool _mouseRotate = false;
        private bool _mouseZoom = false;

        private int _mouseX;
        private int _mouseY;

        public FormDemo()
        {
            InitializeComponent();

            if (!DesignMode)
            {
                _demo = new DemoContext();

                _demo.Initialized += ((o, e) => Init());                
                _demo.KeyDown += ((o, e) => OnKeyPress(e.KeyCode));
                _demo.KeyPress += ((o, e) => OnKeyPress((Keys)e.KeyChar));
                _demo.MouseMove += ((o, e) => OnMouseMove(e.X, e.Y, e.Button));
                _demo.MouseUp += ((o, e) => OnMouseUp(e.Button));

                Controls.Add(_demo);

                _demo.Focus();

                Paint += ((o, e) => Redraw());
                Resize += ((o, e) => OnResize());

                buttonMoveForward.Click += ((o, e) => OnSceneControl(SceneControl.MoveForward));
                buttonMoveBackward.Click += ((o, e) => OnSceneControl(SceneControl.MoveBackward));
                buttonMoveLeft.Click += ((o, e) => OnSceneControl(SceneControl.MoveLeft));
                buttonMoveRight.Click += ((o, e) => OnSceneControl(SceneControl.MoveRight));
                buttonMoveUp.Click += ((o, e) => OnSceneControl(SceneControl.MoveUp));
                buttonMoveDown.Click += ((o, e) => OnSceneControl(SceneControl.MoveUp));
                buttonRotateLeft.Click += ((o, e) => OnSceneControl(SceneControl.RotateLeft));
                buttonRotateRight.Click += ((o, e) => OnSceneControl(SceneControl.RotateRight));
                buttonRotateUp.Click += ((o, e) => OnSceneControl(SceneControl.RotateUp));
                buttonRotateDown.Click += ((o, e) => OnSceneControl(SceneControl.RotateDown));
                buttonZoomIn.Click += ((o, e) => OnSceneControl(SceneControl.ZoomIn));
                buttonZoomOut.Click += ((o, e) => OnSceneControl(SceneControl.ZoomOut));
                buttonScaleInX.Click += ((o, e) => OnSceneControl(SceneControl.ScaleInX));
                buttonScaleInY.Click += ((o, e) => OnSceneControl(SceneControl.ScaleInY));
                buttonScaleInZ.Click += ((o, e) => OnSceneControl(SceneControl.ScaleInZ));
                buttonScaleOutX.Click += ((o, e) => OnSceneControl(SceneControl.ScaleOutX));
                buttonScaleOutY.Click += ((o, e) => OnSceneControl(SceneControl.ScaleOutY));
                buttonScaleOutZ.Click += ((o, e) => OnSceneControl(SceneControl.ScaleOutZ));
                buttonFocus.Click += ((o, e) => OnSceneControl(SceneControl.Focus));

                propertyGrid.PropertyValueChanged += ((o, e) => OnPropertyUpdate(e.ChangedItem.PropertyDescriptor.Name));
            }

            comboBoxControlType.Items.AddRange(Enum.GetValues(typeof(ControlType)).Cast<object>().ToArray());

            cubeToolStripMenuItem.Click += ((o,e) => LoadModel("models/box.obj"));
            piramideToolStripMenuItem.Click += ((o, e) => LoadModel("models/piramide.obj"));
            sphereToolStripMenuItem.Click += ((o, e) => LoadModel("models/sphere.obj"));

            toolStripButtonAbout.Click += ((o, e) => MessageBox.Show(
                "CG homework #1 (Winter 2012)\n\nBy:\n  Oleg Zlotnik\n  Guy Wildman", "About",
                MessageBoxButtons.OK, MessageBoxIcon.Information));
        }
        
        private void Init()
        {
            _cvars = new Cvars(_demo);
            propertyGrid.SelectedObject = _cvars;

            _loadedCameraNames.Add("<Default>");
            _loadedCameraTypes.Add(_cvars.c_type);

            comboBoxControlType.SelectedIndex = (int) ControlType.Camra;

            OnResize();
            UpdateStatusStrip();
        }

        private void UpdateToolstrip()
        {
            foreach (ToolStripButton button in toolStrip.Items)
            {
                if (!(string.IsNullOrEmpty((string) button.Tag))) {
                    CvarWrapper cvar = _demo.GetCvar((string)button.Tag);
                    button.Checked = cvar.GetValueB();
                }
            }
        }

        private void UpdateStatusStrip()
        {
            var camera = _demo.GetActiveCamera();

            float x, y, z;
            camera.GetPosition(out x, out y, out z);

            toolStripStatusLabelCamera.Text = string.Format("Camera at ({0}, {1}, {2})", x, y, z);
        }

        private void UpdatePropertyGrid()
        {
            propertyGrid.Refresh();
        }

        private void SetControlState()
        {
            comboBoxControlInstance.Items.Clear();

            if (_currentControl == ControlType.Camra)
            {
                comboBoxControlInstance.Items.AddRange(_loadedCameraNames.ToArray());

                int activeCamera = _cvars.s_active_camera;
                if (activeCamera >= 0 && activeCamera < comboBoxControlInstance.Items.Count)
                    comboBoxControlInstance.SelectedIndex = activeCamera;

                _cvars.s_active_model = -(_cvars.s_active_model + 1);
            }
            else if (_currentControl == ControlType.Model)
            {
                comboBoxControlInstance.Items.AddRange(_loadedModelFiles.ToArray());

                if (_cvars.s_active_model < 0)
                    _cvars.s_active_model = -(_cvars.s_active_model + 1);

                int activeModel = _cvars.s_active_model;
                if (activeModel >= 0 && activeModel < comboBoxControlInstance.Items.Count)
                    comboBoxControlInstance.SelectedIndex = activeModel;                
            }

            buttonZoomIn.Enabled = _currentControl == ControlType.Camra;
            buttonZoomOut.Enabled = _currentControl == ControlType.Camra;
            buttonScaleInX.Enabled = _currentControl == ControlType.Model;
            buttonScaleInY.Enabled = _currentControl == ControlType.Model;
            buttonScaleInZ.Enabled = _currentControl == ControlType.Model;
            buttonScaleOutX.Enabled = _currentControl == ControlType.Model;
            buttonScaleOutY.Enabled = _currentControl == ControlType.Model;
            buttonScaleOutZ.Enabled = _currentControl == ControlType.Model;
            buttonFocus.Enabled = _currentControl == ControlType.Camra;
        }

        private void Redraw()
        {
            _demo.Draw();
        }

        private void LoadModel()
        {
            using (var dialog = new OpenFileDialog())
            {
                dialog.CheckFileExists = true;
                dialog.Multiselect = false;
                dialog.DefaultExt = ".obj";
                dialog.Filter = "Model files (.obj)|*.obj";

                if (dialog.ShowDialog(this) == DialogResult.OK)
                    LoadModel(dialog.FileName);
            }
        }

        private void LoadModel(string path)
        {
            _demo.LoadMeshModel(path);
            _loadedModelFiles.Add(Path.GetFileName(path));
            _cvars.s_active_model = _loadedModelFiles.Count - 1;
            comboBoxControlType.SelectedIndex = (int)ControlType.Model;

            SetControlState();
            UpdatePropertyGrid();
        }

        private void SwitchToCamera(int index)
        {
            int current = _cvars.s_active_camera;

            if (current >= 0 && current < _loadedCameraTypes.Count)
                _loadedCameraTypes[current] = _cvars.c_type;

            _cvars.s_active_camera = index;

            if (index >= 0 && index < _loadedCameraTypes.Count)
                _cvars.c_type = _loadedCameraTypes[index];
        }

        private void LoadCamera()
        {
            using (var dialog = new AddCameraDialog(string.Format("Camera #{0}", _loadedCameraNames.Count)))
            {
                if (dialog.ShowDialog() == DialogResult.OK)
                {
                    _demo.LoadCamera(
                        dialog.EyeX, dialog.EyeY, dialog.EyeZ,
                        dialog.AtX, dialog.AtY, dialog.AtZ,
                        dialog.UpX, dialog.UpY, dialog.UpZ
                    );

                    _loadedCameraNames.Add(dialog.CameraName);
                    _loadedCameraTypes.Add(_cvars.c_type);

                    SwitchToCamera(_loadedCameraNames.Count - 1);
                    
                    comboBoxControlType.SelectedIndex = (int) ControlType.Camra;

                    SetControlState();
                    UpdateStatusStrip();
                    UpdatePropertyGrid();
                }
            }
        }

        void OnResize()
        {
            _demo.Width = Width - propertyGrid.Width;
            _demo.Height = Height - toolStrip.Height - statusStrip.Height;

            if (_cvars != null)
            {
                _cvars.r_width = _demo.Width;
                _cvars.r_height = _demo.Height;

                _demo.UpdateSize();
                _demo.Draw();

                UpdatePropertyGrid();
            }
        }

        void OnSceneControl(SceneControl type)
        {
            const float MOVE_STEP = 0.1f;            
            const float ROTATE_STEP = 0.2f;
            const float SCALE_STEP = 0.1f;
            const float ZOOM_STEP = 0.1f;

            IControlTarget target = null;
            switch (_currentControl)
            {
                case ControlType.Camra:
                    target = _demo.GetActiveCamera();
                    break;
                case ControlType.Model:
                    target = _demo.GetActiveModel();
                    break;
            }

            switch (type)
            {
                case SceneControl.MoveForward:
                    target.Move(0.0f, 0.0f, MOVE_STEP);
                    break;
                case SceneControl.MoveBackward:
                    target.Move(0.0f, 0.0f, -MOVE_STEP);
                    break;
                case SceneControl.MoveLeft:
                    target.Move(-MOVE_STEP, 0.0f, 0.0f);
                    break;
                case SceneControl.MoveRight:
                    target.Move(MOVE_STEP, 0.0f, 0.0f);
                    break;
                case SceneControl.MoveUp:
                    target.Move(0.0f, MOVE_STEP, 0.0f);
                    break;
                case SceneControl.MoveDown:
                    target.Move(0.0f, -MOVE_STEP, 0.0f);
                    break;
                case SceneControl.RotateLeft:
                    target.Rotate(0.0f, -ROTATE_STEP, 0.0f);
                    break;
                case SceneControl.RotateRight:
                    target.Rotate(0.0f, ROTATE_STEP, 0.0f);
                    break;
                case SceneControl.RotateUp:
                    target.Rotate(ROTATE_STEP, 0.0f, 0.0f);
                    break;
                case SceneControl.RotateDown:
                    target.Rotate(-ROTATE_STEP, 0.0f, 0.0f);
                    break;
                case SceneControl.ScaleInX:
                    target.Scale(1.0f - SCALE_STEP, 1.0f, 1.0f);
                    break;
                case SceneControl.ScaleOutX:
                    target.Scale(1.0f + SCALE_STEP, 1.0f, 1.0f);
                    break;
                case SceneControl.ScaleInY:
                    target.Scale(1.0f, 1.0f - SCALE_STEP, 1.0f);
                    break;
                case SceneControl.ScaleOutY:
                    target.Scale(1.0f, 1.0f + SCALE_STEP, 1.0f);
                    break;
                case SceneControl.ScaleInZ:
                    target.Scale(1.0f, 1.0f, 1.0f - SCALE_STEP);
                    break;
                case SceneControl.ScaleOutZ:
                    target.Scale(1.0f, 1.0f, 1.0f + SCALE_STEP);
                    break;
                case SceneControl.ZoomIn:
                    _cvars.c_zoom += ZOOM_STEP;
                    break;
                case SceneControl.ZoomOut:
                    _cvars.c_zoom -= ZOOM_STEP;
                    break;
                case SceneControl.Focus:
                    var camera = _demo.GetActiveCamera();
                    var model = _demo.GetActiveModel();

                    if (camera != null && model != null) {
                        float mx, my, mz;
                        model.GetPosition(out mx, out my, out mz);
                        
                        float cx, cy, cz;
                        camera.GetPosition(out cx, out cy, out cz);

                        camera.LookAt(cx, cy, cz, mx, my, mz, 0, 1, 0);
                    }
                    break;
            }

            Redraw();
            UpdateStatusStrip();
        }

        private void OnKeyPress(Keys key)
        {
            switch (key)
            {
                case Keys.W:
                    OnSceneControl(SceneControl.MoveForward);
                    break;

                case Keys.S:
                    OnSceneControl(SceneControl.MoveBackward);
                    break;

                case Keys.A:
                    OnSceneControl(SceneControl.MoveLeft);
                    break;

                case Keys.D:
                    OnSceneControl(SceneControl.MoveRight);
                    break;

                case Keys.E:
                    OnSceneControl(SceneControl.MoveUp);
                    break;

                case Keys.Q:
                    OnSceneControl(SceneControl.MoveDown);
                    break;

                case Keys.Left:
                    OnSceneControl(SceneControl.RotateLeft);
                    break;

                case Keys.Right:
                    OnSceneControl(SceneControl.RotateRight);
                    break;

                case Keys.Up:
                    OnSceneControl(SceneControl.RotateUp);
                    break;

                case Keys.Down:
                    OnSceneControl(SceneControl.RotateDown);
                    break;

                case Keys.Oemplus:
                    OnSceneControl(SceneControl.ZoomIn);
                    break;

                case Keys.OemMinus:
                    OnSceneControl(SceneControl.ZoomOut);
                    break;

                case Keys.U:
                    OnSceneControl(SceneControl.ScaleOutX);
                    break;

                case Keys.J:
                    OnSceneControl(SceneControl.ScaleInX);
                    break;

                case Keys.I:
                    OnSceneControl(SceneControl.ScaleOutY);
                    break;

                case Keys.K:
                    OnSceneControl(SceneControl.ScaleInY);
                    break;

                case Keys.O:
                    OnSceneControl(SceneControl.ScaleOutZ);
                    break;

                case Keys.L:
                    OnSceneControl(SceneControl.ScaleInZ);
                    break;
            }
        }

        private void OnMouseMove(int x, int y, MouseButtons buttons)
        {
            if ((buttons & MouseButtons.Left) != 0)
            {
                if (_mouseRotate)
                {
                    CameraWrapper camera = _demo.GetActiveCamera();
                    float sensitivity = _cvars.i_mouse_sensitivity;

                    float dx = (x - _mouseX) * sensitivity;
                    float dy = (y - _mouseY) * sensitivity;

                    camera.Rotate(dy, dx, 0.0f);

                    Redraw();
                    UpdatePropertyGrid();
                    UpdateStatusStrip();
                }
                else
                    _mouseRotate = true;                
            }
            else if ((buttons & MouseButtons.Right) != 0)
            {
                if (_mouseZoom)
                {
                    float sensitivity = _cvars.i_mouse_sensitivity;

                    float dy = -(y - _mouseY) * sensitivity * 0.05f;

                    _cvars.c_zoom += dy;

                    Redraw();
                    UpdatePropertyGrid();
                }
                else
                    _mouseZoom = true;
            }

            _mouseX = x;
            _mouseY = y;
        }

        private void OnMouseUp(MouseButtons buttons)
        {
            if ((buttons & MouseButtons.Left) != 0)
                _mouseRotate = false;

            if ((buttons & MouseButtons.Right) != 0)
                _mouseZoom = false;
        }

        private void OnPropertyUpdate(string name)
        {
            if (name == "r_width" || name == "r_height")
                _demo.UpdateSize();

            UpdateToolstrip();

            _demo.Draw();
        }

        private void OnToolStripButtonClik(object sender, EventArgs e)
        {
            var button = (ToolStripButton) sender;

            if (button == toolStripButtonOpen)
            {
                LoadModel();
            }
            else if (button == toolStripButtonFastLoad)
            {
                contextMenuStripFastLoad.Show(toolStrip, new Point(
                    toolStripButtonFastLoad.Bounds.X + toolStripButtonFastLoad.Bounds.Width,
                    toolStripButtonFastLoad.Bounds.Y + toolStripButtonFastLoad.Bounds.Height)
                );
            }
            else if (button == toolStripButtonAddCamera)
            {
                LoadCamera();
            }
            else
            {
                CvarWrapper cvar = _demo.GetCvar((string)button.Tag);
                cvar.SetValue(!(cvar.GetValueB()));
                button.Checked = cvar.GetValueB();

                UpdatePropertyGrid();
            }
        }

        private void OnComboBoxControlTypeSelectedIndexChanged(object sender, EventArgs e)
        {
            _currentControl = (ControlType) comboBoxControlType.SelectedIndex;

            SetControlState();
            Redraw();
        }

        private void OnComboBoxControlInstanceSelectedIndexChanged(object sender, EventArgs e)
        {
            if (_currentControl == ControlType.Camra)
            {
                SwitchToCamera(comboBoxControlInstance.SelectedIndex);
                UpdatePropertyGrid();
            }
            else if (_currentControl == ControlType.Model)
                _cvars.s_active_model = comboBoxControlInstance.SelectedIndex;

            UpdatePropertyGrid();
            Redraw();
        }
    }
}
